// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.eva.android.widget;

import android.os.Bundle;
import com.eva.framework.dto.DataFromServer;

/**
 * 一个默认提供了无穷进度提示的可查询和显示数据的activity父类.
 * <p>
 * 自v8.1版开始，新增了{@link DataLoadableActivity2} ，该类可方便实现基于ViewModel+LiveData的异步数据加载逻辑。
 *
 * @author xzj
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.1
 */
public abstract class DataLoadableActivity extends ActivityRoot {
    /**
     * 是否在activity被新建时刷新一次界面数据，即调用方
     * 法 {@link #loadData(String...)}1次，true表示是 ，默认是true
     */
    private boolean loadDataOnCreate = true;

    /**
     * {@inheritDoc}
     *
     * @see QueryDataWorder
     * @see #init(Bundle)
     * @see #isLoadDataOnCreate()
     * @see #loadData(String...)
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (isLoadDataOnCreate())
            //是否在activity被新建时刷新一次界面数据
            loadData();
    }

    /**
     * <p>
     * 刷新界面数据的唯一公开方法.<br>
     * 子类实现者如要实现自已的逻辑请重写本方法或其调用的方法.<br>
     * <p>
     * 本方法的默认实现是：在独立的处理线程中调用方法 {@link #queryData(String...)}从服务端查询数据
     * ，成功后再把返回结果传给方法{@link #refreshToView(Object)}来处理.
     * </p>
     *
     * @param params 将要传给 {@link #queryData(String...)}的参数
     */
    public void loadData(String... params) {
        loadData(true, params);
    }

    /**
     * <p>
     * 刷新界面数据的唯一公开方法.<br>
     * 子类实现者如要实现自已的逻辑请重写本方法或其调用的方法.<br>
     * <p>
     * 本方法的默认实现是：在独立的处理线程中调用方法 {@link #queryData(String...)}从服务端查询数据
     * ，成功后再把返回结果传给方法{@link #refreshToView(Object)}来处理.
     * </p>
     *
     * @param showProgress true表示读取网络数据时会显示进度提示，否则不显示
     * @param params       将要传给 {@link #queryData(String...)}的参数
     */
    public void loadData(boolean showProgress, String... params) {
        new QueryDataWorder(showProgress).execute(params);
    }

    /**
     * 当  {@link #loadData(String...)}调用后，服务端处理完成时，如果处理失败（可能是网络不好、或服务端处理出错等）则调用本方法.
     * <p>本方法默认是调用 {@link #finish()}方法以便在处理不成功的情况下退到上一个Activity.
     *
     * @param result 处理完成后服务端返回的处理结果对象，通常会是{@link DataFromServer}对象
     */
    protected void noSucessOnPostExecuteImpl(Object result) {
        DataLoadableActivity.this.finish();
    }

    /**
     * <p>
     * 典型应用场景下，用于客户端查询数据的处理线程（提交请求到服务端并处理返回结果）。<br>
     * <p>
     * 处理与服务端进行数据通信（登陆验证）的多线程（多线程提高用户体验防止阻塞主线程）.<br>
     * 注意：UI的任何操作不能放在多线程里，必须要放在handler里处理，跟swing的单线程原则一样。
     * </p>
     *
     * @see #loadData(String...)
     * @see #queryData(String...)
     */
    private class QueryDataWorder extends DataLoadingAsyncTask<String, Integer, DataFromServer> {
        public QueryDataWorder(boolean showProgress) {
            super(DataLoadableActivity.this);
            this.setShowProgress(showProgress);
        }

        /**
         * @param params 外界传进来的参数
         * @return 查询结果，将传递给onPostExecute(..)方法
         */
        @Override
        protected DataFromServer doInBackground(String... params) {
            // 提交到服务端进行登陆验主并返回用户基本信息
            return queryData(params);
        }

        protected void onPostExecuteImpl(Object result) {
            //** 由js加上：目的是为了在从服务端读取数据出错的情况下自动退回到上一个activity，而不是像以前一样还是进入了本activity，但是此时显示
            //** 数据获取是失败了，而导致界面数据不对，从而影响体验.目前这样改进就是为了在出错了自动回退，给用户重试的机会，要不然进入本界面后用户
			//** 可能就有点慌了（有时候可能会影响操作，比如明细汇总界面里，此时可能会导致数据丢失，而自动回退后，因上一个界面的数据还在，因而可以明确地再来一次）
            if (result != null && result instanceof DataFromServer) {
                if (!((DataFromServer) result).isSuccess())
                    noSucessOnPostExecuteImpl(result);
            }
            //** END

            // 成功后要做的事--
            refreshToView(result);
        }
    }
    //------------------------------------------------------------------------------------------------ END

    /**
     * <p>
     * 本方法将运行于{@link #loadData(String...)}中启动的独立线程中，目的是负责
     * 收集或组织数据，组织好的数据将会被方法{@link #loadData(String...)}传给
     * {@link #refreshToView(Object)}处理以便显示在界面上.<br><br>
     * <p>
     * 典型应用场景下，该类负责连接服务器并且返回结果，但返回结果具体从何而来也可由子类自行决定。<br><br>
     * <p>
     * 典型应用场景下，子类必须实现该方法，并且必须在这个方法里面给ProcessorId，JobDispatchId
     * ，action_id以及oldData（可以为null）和newData（可以为null）赋值 .<br><br>
     *
     * <b>注意：</b>本方法运行于独立的线程中，严禁访问UI组件，否则android将会报错！
     * </p>
     *
     * @param params 由#loadData(String...)传过来的参数
     * @return 典型情况下从服务器端返回的处理对象，子类也可自行封装一个DataFromServer对象
     * ，但请保证 {@link DataFromServer#equals(Object)}==true，否则结果将不会被传给
     * {@link #refreshToView(Object)}进行处理.
     * @see #loadData(String...)
     */
    abstract protected DataFromServer queryData(String... params);

    /**
     * <p>
     * 典型应用场景下，本方法负责把{@link #loadData(String...)}方法中运行的
     * {@link #queryData(String...)}执行结果对象刷新到UI上。但子类也可自行实现自已的数据展现逻辑。<br><br>
     * <p>
     * 典型应用场景下，本方法用于全新刷新UI数据显示，也是唯一提供给子类进行UI数据设置的方法。
     * </p>
     *
     * @param dateToView 典型应用场景下，用来刷新界面数据显示的数据对象
     * @see #loadData(String...)
     */
    abstract protected void refreshToView(Object dateToView);

    /**
     * 是否在activity被新建时刷新一次界面数据，即调用方法 {@link #loadData(String...}1次.
     *
     * @return true表示是 ，默认是true
     */
    public boolean isLoadDataOnCreate() {
        return loadDataOnCreate;
    }

    /**
     * 设置是否在activity被新建时刷新一次界面数据，即调用方法 {@link #loadData(String...)}1次.
     *
     * @param loadDataOnCreate true表示是 ，默认是true
     */
    public void setLoadDataOnCreate(boolean loadDataOnCreate) {
        this.loadDataOnCreate = loadDataOnCreate;
    }
}
